Apparent wind is the wind experienced by a moving object.
The velocity of the apparent wind is the vector sum of the velocity of the headwind (which is the velocity a moving object would experience in still air) plus the velocity of the true wind. The headwind is the additive inverse of the object's velocity; therefore, the velocity of the apparent wind can also be defined as a vector sum of the velocity of the true wind minus the velocity of the object.
Note that a number of additional factors come into play when converting the measurements from the masthead anemometer into the true wind if a high degree of accuracy is required, including the following:
In the presence of a current, the true wind is considered to be that measured on the craft drifting with the water over the bottom, and wind with respect to the sea bed as the ground or geographical wind.
windsurfing and certain types of boats are able to sail faster than the true wind. These include fast and some planing monohulls. ice yachting and land sailing also usually fall into this category, because of their relatively low amount of drag or friction.
The AC72 foiling catamarans used in the America's Cup are an example of this phenomenon, as the boats sail through the water at up to double the environmental wind speed. The effect of this is to radically change the apparent wind direction when sailing "downwind". In these boats the forward speed is so great that the apparent wind is always forward—at an angle that varies between 2 and 4 degrees to the wing sail. This means that AC72's are effectively tacking downwind, although at a greater angle than the normal 45-degree upwind angle, usually between 50 and 70 degrees.TVNZ Live America's cup Broadcast. Interview with Tom Schnackenburg. 22/9/2013
Where:
The angle of apparent wind () can be calculated from the measured velocity of the boat and wind using the inverse cosine in degrees ()
If the velocity of the boat and the velocity and the angle of the apparent wind are known, for instance from a measurement, the true wind velocity and direction can be calculated with:
and
Note: Due to quadrant ambiguity, this equation for is only valid when the apparent winds are coming from the starboard direction (0° < β < 180°). For port apparent winds (180° < β < 360° or 0° > β > -180°), the true pointing angle ( α) has the opposite sign:
Calculation example using python, including simple plot of vectors:
def calculate_apparent_wind(V, W, alpha_deg):
def plot_wind_vectors(V, W, alpha_deg, A, beta_deg, title):
def run_test_cases():
if __name__ == "__main__":
"""
Calculate apparent wind velocity and direction
Parameters:
V: boat speed (m/s)
W: true wind speed (m/s)
alpha_deg: true wind angle in degrees (0 = upwind, 180 = downwind)
Returns:
A: apparent wind speed (m/s)
beta_deg: apparent wind angle in degrees
"""
alpha_rad = math.radians(alpha_deg)
# Calculate apparent wind speed
A = math.sqrt(W**2 + V**2 + 2*W*V*math.cos(alpha_rad))
# Handle zero apparent wind case (should only happen with equal opposite vectors)
if A == 0:
return 0, 0 # No apparent wind, direction undefined
# Calculate apparent wind angle
cos_beta = (W * math.cos(alpha_rad) + V) / A
# Clamp to valid range for arccos
cos_beta = max(-1, min(1, cos_beta))
# Calculate sin_beta to determine the correct quadrant
sin_beta = (W * math.sin(alpha_rad)) / A
# Use atan2 to get the correct angle in full 360° range
beta_rad = math.atan2(sin_beta, cos_beta)
# Convert to degrees and ensure positive angle (0-360°)
beta_deg = math.degrees(beta_rad)
if beta_deg < 0:
beta_deg += 360
return A, beta_deg
"""Plot wind vectors showing true wind, boat velocity, and apparent wind"""
fig, ax = plt.subplots(1, 1, figsize=(8, 6))
# Convert angles to radians for plotting
# Note: angles are measured clockwise from V direction (upward)
# V points upward (positive y), so 0° = upward, 90° = rightward, etc.
alpha_rad = math.radians(alpha_deg)
beta_rad = math.radians(beta_deg)
# Vector components (V points upward, angles measured clockwise from V)
# True wind vector (W) - α is direction wind is COMING FROM (clockwise from V direction)
# So wind vector points opposite to where it's coming from
Wx = -W * math.sin(alpha_rad) # Negative because wind vector points where wind is going
Wy = -W * math.cos(alpha_rad) # Negative because wind vector points where wind is going
# Boat velocity vector (V) - direction boat is GOING TO (upward)
Vx = 0
Vy = V
# H vector = -V (negative boat velocity)
Hx = -Vx
Hy = -Vy
# Apparent wind vector (A) = W + H = W + (-V)
# β is direction apparent wind is COMING FROM, so vector points opposite
# Using calculated values to show the vector sum
Ax_calculated = Wx + Hx
Ay_calculated = Wy + Hy
# For verification: apparent wind vector from β angle (coming from direction)
Ax_from_beta = -A * math.sin(beta_rad) # Negative because coming FROM β direction
Ay_from_beta = -A * math.cos(beta_rad) # Negative because coming FROM β direction
# Plot vectors with specified start/end points
# V starts at (0,0)
ax.quiver(0, 0, Vx, Vy, angles='xy', scale_units='xy', scale=1,
color='red', width=0.005, label=f'Boat Velocity V={V:.1f} m/s (to course)')
# H ends at (0,0), so starts at (-Hx, -Hy)
ax.quiver(-Hx, -Hy, Hx, Hy, angles='xy', scale_units='xy', scale=1,
color='orange', width=0.005, label=f'H = -V (negative boat velocity)')
# A ends at (0,0), so starts at (-Ax_calculated, -Ay_calculated)
ax.quiver(-Ax_calculated, -Ay_calculated, Ax_calculated, Ay_calculated, angles='xy', scale_units='xy', scale=1,
color='green', width=0.005, label=f'Apparent Wind A={A:.1f} m/s (from β={beta_deg:.1f}°)')
# W starts at start of A and ends at end of V
# Start of A: (-Ax_calculated, -Ay_calculated)
# End of V: (Vx, Vy)
# So W vector components: from start of A to end of V
W_start_x = -Ax_calculated
W_start_y = -Ay_calculated
W_vector_x = Vx - W_start_x
W_vector_y = Vy - W_start_y
ax.quiver(W_start_x, W_start_y, W_vector_x, W_vector_y, angles='xy', scale_units='xy', scale=1,
color='blue', width=0.005, label=f'True Wind W={W:.1f} m/s (from α={alpha_deg}°)')
# Set equal aspect ratio and grid with dynamic zoom
# Calculate actual bounds of all vector points
all_x_coords = [0, Vx, -Hx, 0, -Ax_calculated, 0, W_start_x, W_start_x + W_vector_x]
all_y_coords = [0, Vy, -Hy, 0, -Ay_calculated, 0, W_start_y, W_start_y + W_vector_y]
# Find the actual range needed
x_min, x_max = min(all_x_coords), max(all_x_coords)
y_min, y_max = min(all_y_coords), max(all_y_coords)
# Add 15% padding around the actual range
x_range = max(x_max - x_min, 1) # Ensure minimum range of 1
y_range = max(y_max - y_min, 1) # Ensure minimum range of 1
x_padding = x_range * 0.15
y_padding = y_range * 0.15
ax.set_xlim(x_min - x_padding, x_max + x_padding)
ax.set_ylim(y_min - y_padding, y_max + y_padding)
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
ax.legend()
ax.set_xlabel('X (m/s)')
ax.set_ylabel('Y (m/s) - V direction')
ax.set_title(title)
# Add angle annotations at vector midpoints
ax.annotate(f'α={alpha_deg}°', xy=(W_start_x + W_vector_x/2, W_start_y + W_vector_y/2), xytext=(10, 10),
textcoords='offset points', fontsize=10, color='blue')
ax.annotate(f'β={beta_deg:.1f}°', xy=(-Ax_calculated/2, -Ay_calculated/2), xytext=(10, -20),
textcoords='offset points', fontsize=10, color='green')
return fig
"""Run test cases that can be verified by hand"""
test_cases = [
# (V, W, alpha, description)
(0, 10, 90, "Stationary boat, wind from side"),
(10, 0, 0, "No wind, boat moving"),
(5, 5, 0, "Headwind: boat and wind same speed"),
(5, 5, 180, "Tailwind: boat and wind same speed"),
(10, 10, 90, "Beam wind: boat and wind same speed"),
(5, 10, 45, "Wind from 45° angle"),
(8, 12, 120, "Wind from 120° angle"),
(10, 10, 120, "Wind from 120° angle, same speeds"),
(10, 10, 270, "Wind from 270° angle, same speeds"),
(10, 10, 300, "Wind from 300° angle, same speeds"),
]
print("Apparent Wind Calculations")
print("=" * 70)
print(f"{'Case':<4} {'V (m/s)':<8} {'W (m/s)':<8} {'α (deg)':<8} {'A (m/s)':<10} {'β (deg)':<10} {'Description'}")
print("-" * 70)
figures = []
for i, (V, W, alpha, description) in enumerate(test_cases, 1):
A, beta = calculate_apparent_wind(V, W, alpha)
print(f"{i:<4} {V:<8} {W:<8} {alpha:<8} {A:<10.2f} {beta:<10.1f} {description}")
# Create plot for this case
title = f"Case {i}: {description}\nV={V} m/s, W={W} m/s, α={alpha}° → A={A:.2f} m/s, β={beta:.1f}°"
fig = plot_wind_vectors(V, W, alpha, A, beta, title)
figures.append(fig)
# Hand verification examples
print("\n" + "=" * 60)
print("Hand Verification:")
print("=" * 60)
# Case 1: Stationary boat (V=0)
print("1. Stationary boat (V=0): A should equal W, β should equal α")
V, W, alpha = 0, 10, 90
A, beta = calculate_apparent_wind(V, W, alpha)
print(f" Input: V={V}, W={W}, α={alpha}° → Output: A={A:.2f}, β={beta:.1f}°")
print(f" Expected: A={W}, β={alpha}° OK" if abs(A-W) < 0.01 and abs(beta-alpha) < 0.1 else " NOT OK")
# Case 2: No wind (W=0)
print("\n2. No wind (W=0): A should equal V, β should be 0°")
V, W, alpha = 10, 0, 0
A, beta = calculate_apparent_wind(V, W, alpha)
print(f" Input: V={V}, W={W}, α={alpha}° → Output: A={A:.2f}, β={beta:.1f}°")
print(f" Expected: A={V}, β=0° OK" if abs(A-V) < 0.01 and abs(beta-0) < 0.1 else " NOT OK")
# Case 3: Headwind, equal speeds
print("\n3. Headwind equal speeds: A should be V+W")
V, W, alpha = 5, 5, 0
A, beta = calculate_apparent_wind(V, W, alpha)
print(f" Input: V={V}, W={W}, α={alpha}° → Output: A={A:.2f}, β={beta:.1f}°")
print(f" Expected: A={V+W}=10, β=0° OK" if abs(A-10) < 0.01 and abs(beta-0) < 0.1 else " NOT OK")
# Case 4: Tailwind, equal speeds
print("\n4. Tailwind equal speeds: A should be |W-V|")
V, W, alpha = 5, 5, 180
A, beta = calculate_apparent_wind(V, W, alpha)
print(f" Input: V={V}, W={W}, α={alpha}° → Output: A={A:.2f}, β={beta:.1f}°")
print(f" Expected: A={abs(W-V)}=0, β undefined OK" if abs(A-0) < 0.01 else " NOT OK")
return figures
# Run test cases
figures = run_test_cases()
# Show plots
plt.show()
print(f"\nGenerated {len(figures)} plots showing vector relationships.")
print("\nFormulas used:")
print("A = √(W² + V² + 2WV·cos(α))")
print("β = arccos((W·cos(α) + V) / A)")
print("\nWhere:")
print("V = boat speed (m/s)")
print("W = true wind speed (m/s)")
print("α = true wind angle (0° = headwind, 180° = tailwind)")
print("A = apparent wind speed (m/s)")
print("β = apparent wind angle (degrees)")
|
|